require "calcula/version"

# This module contains the lexer and the parser for Calcula. In the future, the
# runtime aspect will be included also.
#
# @author Paul T.
module Calcula

  # This module contains all the tree expressions being created when the
  # parse method is called. All valid tree expressions must subclass `Calcula::Expr`
  #
  # @see Calcula::Expr
  # @author Paul T.
  module Exprs
    # The module here is only for documenting only. The real stuff is under ./Exprs
  end

  require "Token"
  require "Lexer"
  require "Parser"

  # Short hand for calling `Calcula::Lexer.new(txt).lex`
  #
  # @see Calcula::Lexer#lex
  # @param txt [String] The Calcula source code
  # @return [Array<Calcula::Token>] Generated based on the source code
  def Calcula::lex(txt)
    Lexer.new(txt).lex
  end

  # Short hand for calling `Calcula::Parser.new(toks).parse` repetitively.
  #
  # @see Calcula::Parser#parse
  # @param toks [Array<Calcula::Token>] This should be supplied by `Calcula::lex`.
  # @return [Array<Calcula::Expr>] Multiple parse trees
  def Calcula::parse(toks)
    rst = []
    parser = Parser.new(toks)
    while (r = parser.parse) != nil do
      rst << r
    end
    return rst
  end

  # Essential declarations so the Calcula to Ruby option will work
  #
  # @return [String] The declarations
  def Calcula::stdRubyHeader
    txt = <<-EOT
class Numeric
  def call(x)   # Allowing 10.(20) to work (becomes 200)
    self * x
  end
end
class Proc
  def +(g)
    ->(*args){ self.(*args) + g.(*args) }
  end
  def -(g)
    ->(*args){ self.(*args) - g.(*args) }
  end
  def *(g)
    ->(*args){ self.(*args) * g.(*args) }
  end
  def /(g)
    ->(*args){ self.(*args) / g.(*args) }
  end
  def %(g)
    ->(*args){ self.(*args) % g.(*args) }
  end
  def **(g)
    ->(*args){ self.(*args) ** g.(*args) }
  end
end
    EOT
    return txt
  end

  # Combines the essential declarations with the compiled Calcula code together
  #
  # @param src [String] The Calcula source code
  # @return [String] The complete executable ruby code
  def Calcula::compile(src)
    <<-EOS
#{Calcula::stdRubyHeader}
# CALCULA SOURCE CODE STARTS HERE
#{Calcula::parse(Calcula::lex(src)).collect{ |e| e.to_s(form: :ruby) }.join("\n")}
    EOS
  end
end
